package auctionatr

/**
 * Diese Klasse ist der entsprechende Controller zur Model-class Auction. Alle Methoden, die in Verbindung mit einer Auktion stehen, werden u.a. in dieser Klasse gesteuert.
 * 
 * @author Can Paul
 *
 */

class AuctionController extends BaseController {

	/**
	 * Navigationsleiste mit den Elementen "alle auktionen", "neue auktion" und "login".
	 * Identisch mit Category Menü
	 */
	static navigation = [
		group: 'main',
		order:3,
		title:'auktionen',
		path: 'auction',
		subItems:
		[
			[order: 1, title: 'alleauktionen', action: 'list'],
			[order: 2, title: 'neueauktion', action: 'create'],
			[order: 2, title: 'allekategorien', action: 'listCategories'],
			[order: 2, title: 'zeigekategorie', action: 'showCategory', isVisible: {false} ],
			[order: 3, title: 'login', action: 'login', isVisible: { session.username == null }],
			// Max: um sicherzustellen, dass Menue korrekt angezeigt wird
			[action: 'save', isVisible: { false }],
			[title: 'auktionanzeigen', action: 'show', isVisible: { false }]
		]
	]

	/**
	 * <!-- TODO -->
	 */
	static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

	/**
	 * Die Index-Seite wird standardmäßig aufgerufen. Der User wird automatisch auf die Liste aller Auktionen weitergeleitet. 
	 */
	def index = {
		redirect(action: "list", params: params)
	}
		
	/**
	 * Darstellung einer Liste aller Auktionen. Auf jeder Seite werden 10 Auktionen angezeigt (auf der ersten Seite maximal 10). Bei mehr als 10 Auktionen werden die nächsten 10 Auktionen auf der nächsten Seite angezeigt.
	 * Die Seitenzahlen sowie "Vorherige" und "Nächste", die unter der Liste zu finden sind, dienen zur Navigation zwischen den verschiedenen Seiten.
	 */
	def list = {
		// Darstellung einer Liste aller Auktionen
		params.max = Math.min(params.max ? params.int('max') : 10, 100)
		[auctionInstanceList: Auction.list(params), auctionInstanceTotal: Auction.count()]
	}
	
	
	/**
	 * starts the searchable plugin service, indexing the auction data
	 */
	def searchableService
	
	/**
	 * Suche auf die Auktionen (Volltext) ausführen und das Ergebnis dann in die list-View übergeben
	 */
	def search = {
		def query = params.q
		if(query){
			query = query+"*~" //ermöglicht eine automatische unscharfe Suche
			def srchResults = searchableService.search(query)
			render(view: "list",
					model: [auctionInstanceList: srchResults.results,
						auctionInstanceTotal:srchResults.total])
		}else{
			redirect(action: "list")
		}
	}

	/** Seite zur Erstellung einer neuen Auktion */
	def create = {
		def auctionInstance = new Auction()
		auctionInstance.properties = params
		return [auctionInstance: auctionInstance]
	}

	/**
	 * Beim Klick auf "Anlegen" werden die eingegebenen Daten für die Auktion abgespeichert.
	 * Als Bieter wird der Auktion der aktuell eingeloggte User zugewiesen. 
	 * Nach erfolgreichem Anlegen der Auktion gelangt der User zur Auktionsansicht und es erscheint eine Bestätigungsbenachrichtigung, dass die Auktion erfolgreich angelegt wurde. Wenn eine Auktion nicht erfolgreich angelegt werden konnte, erscheint eine Fehlermeldung und der User bleibt auf der Erstellungsseite.
	 */
	def save = {

		// neue Instanz einer Auktion mit Parametern aus POST-Request:
		def auctionInstance = new Auction(params)

		def userInstance = User.get(session?.userid)
		auctionInstance.properties['users'] = userInstance

		def f = request.getFile('picture') //PF: This extracts the image file from the HTTP upload request

		if (!f.empty) { // falls Bild hochgeladen wurde:

			def accepted = [
				'image/png',
				'image/jpeg',
				'image/gif'] //Contains the accepted filetypes
			// Max: wenn Dateityp nicht gültig, dann Error definieren und nicht speichern
			if (!accepted.contains(f.getContentType())){

				// auch alle anderen Felder validieren:
				auctionInstance.validate()

				// danach manuell den Fehler für falschen Dateitypen hinzufügen:
				auctionInstance.errors.rejectValue("picture","default.file.wrongType.message")

				// Create view neu rendern, danach Methode beenden
				render(view: "create", model: [auctionInstance: auctionInstance])
				return;
			}

			// Bild in auctionInstance speichern
			auctionInstance.picture = f.getBytes() //Copy the bytestream into the auction object
			auctionInstance.pictureType = f.getContentType() //Copy the filetype into the user object
		} else { // falls kein Bild hochgeladen wurde, null setzen und Vorgang fortsetzen
			auctionInstance.picture = null
			auctionInstance.pictureType = null
		}

		// Speichern:
		if (auctionInstance.save(flush: true)) {
			flash.message = "${message(code: 'default.created.message', args: [message(code: 'auction.label', default: 'Auction'), auctionInstance.id])}"
			redirect(action: "show", id: auctionInstance.id)
		}
		else { // Falls Fehler: create View erneut anzeigen
			render(view: "create", model: [auctionInstance: auctionInstance])
		}
	}

	/**
	 * Diese Ansicht zeigt alle Informationen zu einer Auktion.
	 * 
	 */
	def show = {
		// Aktuelle Auktion wird der Variablen auctionInstance zugewiesen.
		def auctionInstance = Auction.get(params.id)
		if (!auctionInstance) { // falls Auktion mit gegebener ID nicht gefunden:
			flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'auction.label', default: 'Auction'), params.id])}"
			redirect(action: "list")
		}
		else {
			// Höchstgebot ermitteln
			def lastBid = Bid.findByAuctions(auctionInstance, [max: 1, order: 'desc', sort: 'gebot'])
			// Wenn kein Gebot vorhanden, dann Startpreis der Auktion anzeigen:
			if(lastBid == null) {
				lastBid = new Bid()
				lastBid['gebot'] = auctionInstance.startPrice
			}

			[auctionInstance: auctionInstance, lastBid : lastBid]
		}
	}


	/**
	 * Darstellung des Auktionsbildes
	 * 
	 */
	def auctionImage={
		// Darstellung des Auktionsbildes
		def auctionInstance= Auction.get(params.id) //everything OK?

		if(!auctionInstance || !auctionInstance.picture|| !auctionInstance.pictureType){ // falls kein Bild darstellbar
			response.sendError(404) //send 404 error (file not found)
			return;
		}

		// senden des Streams an Browser
		response.setContentType(auctionInstance.pictureType) //PF: these 5 lines build the HTML statement
		response.setContentLength(auctionInstance.picture.size())
		OutputStream out = response.getOutputStream();
		out.write(auctionInstance.picture);
		out.close();

	}

	/**
	 * Diese Methode löscht eine Auktion. Nur der Administrator kann eine Auktion löschen.
	 */
	def delete = {
		// Auktion löschen: nur für Admin möglich
		if(session?.admin == true) {
			def auctionInstance = Auction.get(params.id)
			// falls zu löschende Auktion gefunden:
			if (auctionInstance) {
				try {
					auctionInstance.delete(flush: true)
					flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'auction.label', default: 'Auction'), params.id])}"
					redirect(action: "list")
				}
				catch (org.springframework.dao.DataIntegrityViolationException e) {
					flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'auction.label', default: 'Auction'), params.id])}"
					redirect(action: "show", id: params.id)
				}
			}
			else { // Auktion nicht gefunden
				flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'auction.label', default: 'Auction'), params.id])}"
				redirect(action: "list")
			}
		} else { // falls nicht Admin zur Auktion weiterleiten:
			flash['message'] = "${message(code: 'default.accessdenied.message')}"
			redirect(action: "show", params: [id: params.id])
		}
	}

	/**
	 * Erstellt einen Report über den Auktionsverlauf. 
	 * Der Report beinhaltet eine grafische Darstellung aller Gebote, die für diese Auktionen abgegeben wurden. Dargestellt werden die Gebotshöhe und der Abgabezeitpunkt.
	 * Des Weiteren enthält der Report weitere Informationen zur Auktion wie Gewinner der Auktion, Adresse des Gewinners, Verkaufspreis, etc.
	 * 
	 */
	def report={
		def auctionInstance = Auction.get(params.id)
		// Soll nur von Admin oder Ersteller angezeigt werden
		if(session?.userid == auctionInstance.users.id || session?.admin ==true) {
			if (false){		//TODO auction not yet finished
				flash.message = "${message(code:'auction.finish.stillRunning.message')}"
				redirect(action:"show", params: [id: params.id])
			}
			else{
				//Darf auch nur angezeigt werden, wenn bereits Bieter vorhanden sind
				if(auctionInstance.bids){
					def bidList = Bid.findAllByAuctions(auctionInstance, [order:'desc', sort:'gebot'])
					def bidListAsc = Bid.findAllByAuctions(auctionInstance, [order:'asc', sort:'gebot'])
					def winningBid = Bid.findByAuctions(auctionInstance, [max: 1, order: 'desc', sort: 'gebot'])
					def winningUser = winningBid.users
					[auctionInstance:auctionInstance, bidList:bidList,bidListAsc:bidListAsc, winningBid:winningBid, winningUser:winningUser]
				}
				else{
					flash.message="${message(code:'auction.finish.noBids.message')}"
					redirect(action:"show", params: [id: params.id])

				}
			}
		}else{
			flash.message="${message(code:'default.accessdenied.message')}"
			redirect(action:"show", params: [id: params.id])
		}
	}
	
	/**
	 * Liste aller Kategorien
	 */
	def listCategories = {
		params.max = Math.min(params.max ? params.int('max') : 10, 100)
		[categoryInstanceList: Category.list(params), categoryInstanceTotal: Category.count()]
	}

	/**
	 * Anzeigen aller Auktionen einer Kategorie
	 */
	def showCategory = {
		def max = (params.max ? params.int('max') : 10)
		def offset = (params.offset ? params.int('offset') : 0)
		// finde alle Auktionen einer bestimmten Kategorie
		def categoryInstance = Category.get(params.id)
		def auctionInstanceList = Auction.executeQuery("select id, a.name, description, finishDate, startPrice,  (SELECT max(gebot) FROM Bid WHERE auctions = a.id) as gebot FROM Auction as a WHERE a.categories = ? ORDER BY a.id DESC", [categoryInstance], [max: max, offset: offset])
		def auctionInstanceCount = Auction.executeQuery("select id FROM Auction as a WHERE a.categories = ? ORDER BY a.id DESC", [categoryInstance])

		[categoryInstance: categoryInstance, auctionInstanceList: auctionInstanceList, auctionInstanceTotal: auctionInstanceCount.size()]
	}
	
	/**
	 * Vor dem Aufruf einer Seite muss überprüft werden, ob ein User eingeloggt ist. Mit dieser Methode wird festgelegt, für welche Seiten eine Authentifizierung nicht notwendig ist.
	 */
	def beforeInterceptor = [action:this.&auth,except:[
			// Authentifizierung notwendig für alle, ausser:
			'save',
			'list',
			'search',
			'index',
			'show',
			'auctionImage',
			'showCategory',
		]]

	/**
	 * Login-Seite
	 */
	def login = {
		redirect(controller: 'user', action:'login')
	}


}